﻿local pont = 0.000
local pofft = 0.000
local ont = 0.000
local offt = 0.000
local ons = 0.000
local offs = 0.000
local offh = 0
local onh = 0
local epont = 0.000
local epofft = 0.000
local eont = 0.000
local eofft = 0.000
local eons = 0.000
local eoffs = 0.000
local eoffh = 0
local eonh = 0
local testvar = 0
local math_mod = math.fmod or math.mod

local semount = AceLibrary("SpecialEvents-Mount-2.0")

local last_auto_hit = 0
local cast_broken

if not (AttackBarDB) then
    AttackBarDB = {}
end

local function to_string(str)
    if str ~= nil then
        if type(str) == "table" then
            return "<table>";
        elseif type(str) == "function" then
            return "<function>";
        else
            return tostring(str)
        end
    else
        return "<nil>"
    end
end

local function debug_print(txt)
    local _, class = UnitClass("player")
    local color = RAID_CLASS_COLORS[class].colorStr

    if (DEFAULT_CHAT_FRAME) then
        DEFAULT_CHAT_FRAME:AddMessage("|cffffd200A|c" .. color .. "bar|r - " .. txt)
    end
end

function Abar_loaded()
    SlashCmdList["ATKBAR"] = Abar_chat_cmd;
    SLASH_ATKBAR1 = "/abar";
    SLASH_ATKBAR2 = "/atkbar";
    if AttackBarDB.range == nil then
        AttackBarDB.range = true
    end
    if AttackBarDB.h2h == nil then
        AttackBarDB.h2h = true
    end
    if AttackBarDB.timer == nil then
        AttackBarDB.timer = true
    end
    if AttackBarDB.mob == nil then
        AttackBarDB.mob = false
    end
    if AttackBarDB.pvp == nil then
        AttackBarDB.pvp = true
    end
    if AttackBarDB.text == nil then
        AttackBarDB.pvp = "standard"
    end
    Abar_Mhr:SetPoint("LEFT", Abar_Frame, "TOPLEFT", 6, -13)
    Abar_Oh:SetPoint("LEFT", Abar_Frame, "TOPLEFT", 6, -38)
    Abar_MhrText:SetJustifyH("Left")
    Abar_OhText:SetJustifyH("Left")

    local fontName, fontSize = Abar_MhrText:GetFont()
    Abar_MhrText:SetFont(fontName, 11, "OUTLINE")
    Abar_OhText:SetFont(fontName, 11, "OUTLINE")

    Abar_MhrTime:SetFont(fontName, 11, "OUTLINE")
    Abar_OhTime:SetFont(fontName, 11, "OUTLINE")

    ebar_VL()

    if AttackBarDB.text == "thin" then
        --
    elseif AttackBarDB.text == "none" then
        --
    else
        AttackBarDB.text = "standard"
    end
end

function Abar_chat_cmd(msg)
    msg = strlower(msg)
    if msg == "fix" then
        Abar_reset()
    elseif msg == "lock" then
        Abar_Frame:Hide()
        ebar_Frame:Hide()
    elseif msg == "unlock" then
        Abar_Frame:Show()
        ebar_Frame:Show()
    elseif msg == "range" then
        AttackBarDB.range = not (AttackBarDB.range)
        DEFAULT_CHAT_FRAME:AddMessage('range is' .. Abar_Boo(AttackBarDB.range));
    elseif msg == "h2h" then
        AttackBarDB.h2h = not (AttackBarDB.h2h)
        DEFAULT_CHAT_FRAME:AddMessage('H2H is' .. Abar_Boo(AttackBarDB.h2h));
    elseif msg == "timer" then
        AttackBarDB.timer = not (AttackBarDB.timer)
        DEFAULT_CHAT_FRAME:AddMessage('timer is' .. Abar_Boo(AttackBarDB.timer));
    elseif msg == "pvp" then
        AttackBarDB.pvp = not (AttackBarDB.pvp)
        DEFAULT_CHAT_FRAME:AddMessage('pvp is' .. Abar_Boo(AttackBarDB.pvp));
    elseif msg == "text" then
        if AttackBarDB.text == "standard" then
            AttackBarDB.text = "thin"
        elseif AttackBarDB.text == "thin" then
            AttackBarDB.text = "none"
        else
            AttackBarDB.text = "standard"
        end
        DEFAULT_CHAT_FRAME:AddMessage("Attack bar textures are " .. AttackBarDB.text)
    elseif msg == "mob" then
        AttackBarDB.mob = not (AttackBarDB.mob)
        DEFAULT_CHAT_FRAME:AddMessage('mobs are' .. Abar_Boo(AttackBarDB.mob));
    else
        Abar_usage();
    end
end

function Abar_usage()
    DEFAULT_CHAT_FRAME:AddMessage('Abar 支持以下命令:');
    DEFAULT_CHAT_FRAME:AddMessage('Lock - 锁定锚点');
    DEFAULT_CHAT_FRAME:AddMessage('unlock - 解锁锚点');
    DEFAULT_CHAT_FRAME:AddMessage('fix - 若要重置值，它们是否会出错?, 攻击后等待5秒使用此命令');
    DEFAULT_CHAT_FRAME:AddMessage('h2h - 打开和关闭近战的条(s)');
    DEFAULT_CHAT_FRAME:AddMessage('range - 打开和关闭的远程的条');
    DEFAULT_CHAT_FRAME:AddMessage('pvp - 打开和关闭敌人的玩家的条(s)');
    DEFAULT_CHAT_FRAME:AddMessage('mob - 打开和关闭敌人怪物的条(s)');
    DEFAULT_CHAT_FRAME:AddMessage('text - 打开和关闭条上的文字');
end

local combatSpells = {
    ["英勇打击"] = 1,
    ["顺劈斩"] = 1,
    ["猛击"] = 1,
    ["猛禽一击"] = 1,
    ["槌击"] = 1,
}

local combatStrings = {
    SPELLLOGSELFOTHER,          -- 你的%s击中%s造成%d点伤害。
    SPELLLOGCRITSELFOTHER,      -- 你的%s对%s造成%d的致命一击伤害。
    SPELLDODGEDSELFOTHER,       -- 你的%s被%s躲闪过去了。
    SPELLPARRIEDSELFOTHER,      -- 你的%s被%s招架了。
    SPELLMISSSELFOTHER,         -- 你的%s没有击中%s。
    SPELLBLOCKEDSELFOTHER,      -- 你的%s被%s格挡了。
    SPELLDEFLECTEDSELFOTHER,    -- 你的%s被%s偏斜了。
    SPELLEVADEDSELFOTHER,       -- 你的%s被%s闪避过去了。
    SPELLIMMUNESELFOTHER,       -- 你的%s施放失败。%s对此免疫。
    SPELLLOGABSORBSELFOTHER,    -- 你的%s被%s吸收了。
    SPELLREFLECTSELFOTHER,      -- 你的%s被%s反弹回来。
    SPELLRESISTSELFOTHER,       -- 你的%s被%s抵抗了。
    SPELLLOGCRITSELFSELF,       -- 你的%s致命一击对你造成%d点伤害。
    IMMUNESPELLSELFOTHER,       -- %s对你的%s免疫。
}

for index in combatStrings do
    for _, pattern in {"%%s", "%%d"} do
        combatStrings[index] = string.gsub(combatStrings[index], pattern, "(.+)")
    end
end

--
-- See: https://www.gammon.com.au/scripts/doc.php?lua=string.find
--

function Abar_parse_spell(msg)
    -- 优先查找命中率高的模式
    local target
    local a, b, spell = string.find(msg, "你的(.+)击中")
    -- 只考虑物理暴击, 忽略法术暴击
    if not spell then
        a, b, spell = string.find(msg, "你的(.+)对(.+)造成(.+)的致命一击")
    end
    if not spell then
        a, b, spell = string.find(msg, "你的(.+)被")
    end
    if not spell then
        a, b, spell = string.find(msg, "你的(.+)没有击中")
    end
    if not spell then
        a, b, spell = string.find(msg, "你的(.+)施放失败")
    end
    -- 忽略法术免疫
    --if not spell then
    --    a, b, target, spell = string.find(msg, "(.+)对你的(.+)免疫")
    --end
    return spell
end

-- 该版本效率可能差一点, 但是各语言版本通用.
function Abar_parse_spell_v2(msg)
    for _, str in combatStrings do
        -- 即使是匹配 IMMUNESPELLSELFOTHER: "%s对你的%s免疫。",
        -- 问题也不是很大, 除非目标的名字跟技能名一样 ?
        local _, _, spell = string.find(msg, str)
        if (not spell) then
            return spell
        end
    end
end

function Abar_self_hit(arg1)
    local spell = Abar_parse_spell(arg1)
    if spell then
        Abar_self_spell_hit(spell);
    else
        ons, offs = UnitAttackSpeed("player");
        local hd, ld, ohd, old = UnitDamage("player")
        hd, ld = hd - math_mod(hd, 1), ld - math_mod(ld, 1)
        if old then
            ohd, old = ohd - math_mod(ohd, 1), old - math_mod(old, 1)
        end
        local tons
        if offs then
            ont, offt = GetTime(), GetTime()
            if ((math.abs((ont - pont) - ons) <= math.abs((offt - pofft) - offs)) and not (onh <= offs / ons)) or offh >=
                ons / offs then
                if pofft == 0 then
                    pofft = offt
                end
                pont = ont
                tons = ons
                offh = 0
                onh = onh + 1
                ons = ons - math_mod(ons, 0.01)
                Abar_Mhrs(tons, "主手 [" .. ons .. "秒] (" .. hd .. "-" .. ld .. ")", 0, 0, 1.0)
            else
                pofft = offt
                offh = offh + 1
                onh = 0
                ohd, old = ohd - math_mod(ohd, 1), old - math_mod(old, 1)
                offs = offs - math_mod(offs, 0.01)
                Abar_Ohs(offs, "副手 [" .. offs .. "秒] (" .. ohd .. "-" .. old .. ")", 1.0, 0.1, 0.1)
            end
        else
            ont = GetTime()
            tons = ons
            ons = ons - math_mod(ons, 0.01)
            Abar_Mhrs(tons, "主手 [" .. ons .. "秒] (" .. hd .. "-" .. ld .. ")", 0, 0, 1.0)
        end
    end
end

function Abar_reset()
    pont = 0.000
    pofft = 0.000
    ont = 0.000
    offt = 0.000
    local onid = 0
    local offid = 0
end

function Abar_self_spell_hit(arg1)
    local spell = Abar_parse_spell(arg1)
    if not spell then
        spell = arg1
    end

    local rs, rhd, rld = UnitRangedDamage("player");
    local rhd, rld = rhd - math_mod(rhd, 1), rld - math_mod(rld, 1)
    local trs
    if spell == "自动射击" and AttackBarDB.range == true then
        trs = rs
        rs = rs - math_mod(rs, 0.01)
        Abar_Mhrs(trs, "自动射击 [" .. rs .. "秒] (" .. rhd .. "-" .. rld .. ")", 0, 1, 0)
    elseif (spell == "Shoot" or spell == "射击") and AttackBarDB.range == true then
        trs = rs
        rs = rs - math_mod(rs, 0.01)
        Abar_Mhrs(trs, "魔杖 [" .. ons .. "s] (" .. rhd .. "-" .. rld .. ")", .7, .1, 1)
    elseif (spell == "猛禽一击" or spell == "英勇打击" or spell == "槌击" or spell == "顺劈斩" or spell ==
        "猛击") and (AttackBarDB.h2h == true) then
        local hd, ld, ohd, lhd = UnitDamage("player")
        hd, ld = hd - math_mod(hd, 1), ld - math_mod(ld, 1)
        if pofft == 0 then
            pofft = offt
        end
        pont = ont
        local tons = ons
        ons = ons - math_mod(ons, 0.01)
        Abar_Mhrs(tons, "主手 [" .. ons .. "秒] (" .. hd .. "-" .. ld .. ")", 0, 0, 1)
    end
end

function Abar_spell_direct(spellName)
    local mount = semount:PlayerOnMount()

    if AttackBarDB.range and (not mount) and (not cast_broken) then
        local a, b, sparse = string.find(spellName, "(.+)%(")
        if sparse then
            spellName = sparse
        end
        local rs, rhd, rld = UnitRangedDamage("player");
        rhd, rld = rhd - math_mod(rhd, 1), rld - math_mod(rld, 1)
        local trs
        if spellName == "投掷" then
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "投掷 [" .. (rs) .. "秒] (" .. rhd .. "-" .. rld .. ")", 1, .5, 0)
        elseif spellName == "射击" then
            rs = UnitRangedDamage("player")
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "远程 [" .. (rs) .. "秒] (" .. rhd .. "-" .. rld .. ")", .5, 0, 1)
        elseif spellName == "弓射击" then
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "弓 [" .. (rs) .. "秒] (" .. rhd .. "-" .. rld .. ")", 1, .5, 0)
        elseif spellName == "枪械射击" then
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "枪 [" .. (rs) .. "秒] (" .. rhd .. "-" .. rld .. ")", 1, .5, 0)
        elseif spellName == "弩射击" then
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "弩 [" .. (rs) .. "秒] (" .. rhd .. "-" .. rld .. ")", 1, .5, 0)
        elseif spellName == "瞄准射击" then
            trs = rs
            rs = rs - math_mod(rs, 0.01)
            Abar_Mhrs(trs - 1, "瞄准射击 [" .. (rs) .. "秒]", 1, .1, .1)
            cast_broken = true
        elseif spellName == "多重射击" then
            trs = 0.5
            rs = 0.5
            Abar_Mhrs(trs, "多重射击 [" .. (rs) .. "秒]", 1, .1, .1)
            cast_broken = true
        end
    end
end

function Abar_event(event)
    if (event == "CHAT_MSG_COMBAT_SELF_MISSES" or event == "CHAT_MSG_COMBAT_SELF_HITS") and AttackBarDB.h2h == true then
        Abar_self_hit(arg1)
    elseif event == "PLAYER_LEAVE_COMBAT" then
        --debug_print("event = "..event..", arg1 = "..to_string(arg1))
        Abar_reset()
    elseif event == "VARIABLES_LOADED" then
        Abar_loaded()
    elseif event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
        Abar_self_spell_hit(arg1)
    elseif event == "SPELLCAST_INTERRUPTED" then
        Abar_Mhr:Hide()
        Abar_Oh:Hide()
    elseif event == "SPELLCAST_STOP" then
        cast_broken = nil
    --[[
    elseif event == "UNIT_SPELLCAST_SENT" then
        Abar_spell_direct(arg2)
    --]]
    end
end

function Abar_Update()
    local nowtime = GetTime()
    local left = 0.00
    local tSpark = getglobal(this:GetName() .. "Spark")
    local tTime = getglobal(this:GetName() .. "Time")
    if AttackBarDB.timer == true then
        left = (this.et - GetTime()) - (math_mod((this.et - GetTime()), .1))
        -- tText:SetText(this.txt.. "{"..left.."}")
        tTime:SetText(left)
        tTime:SetPoint("LEFT", this, "LEFT", 114, 5);
        tTime:Show()
    else
        tTime:Hide()
    end
    this:SetValue(nowtime)
    tSpark:SetPoint("CENTER", this, "LEFT", (nowtime - this.st) / (this.et - this.st) * 200, 2);
    if nowtime >= this.et then
        this:Hide()
        tSpark:SetPoint("CENTER", this, "LEFT", 200, 2);
    end
end

function Abar_Mhrs(barTime, text, r, g, b)
    Abar_Mhr:Hide()
    Abar_Mhr.txt = text
    -- 减去网络延迟
    local _, _, latencyAb = GetNetStats()
    Abar_Mhr.st = GetTime() - (latencyAb / 1000)
    Abar_Mhr.et = GetTime() + barTime
    Abar_Mhr:SetStatusBarColor(r, g, b)
    Abar_MhrText:SetText(text)
    Abar_Mhr:SetMinMaxValues(Abar_Mhr.st, Abar_Mhr.et)
    Abar_Mhr:SetValue(Abar_Mhr.st)
    Abar_Mhr:Show()
end

function Abar_Ohs(barTime, text, r, g, b)
    Abar_Oh:Hide()
    Abar_Oh.txt = text
    -- 减去网络延迟
    local _, _, latencyAb = GetNetStats()
    Abar_Oh.st = GetTime() - (latencyAb / 1000)
    Abar_Oh.et = GetTime() + barTime
    Abar_Oh:SetStatusBarColor(r, g, b)
    Abar_OhText:SetText(text)
    Abar_Oh:SetMinMaxValues(Abar_Oh.st, Abar_Oh.et)
    Abar_Oh:SetValue(Abar_Oh.st)
    Abar_Oh:Show()
end

function Abar_Boo(input)
    if input == true then
        return " ON"
    else
        return " OFF"
    end
end

-- Hook: CastSpellByName(spellName, onSelf)
Abar_OrigCastSpellByName = CastSpellByName

function Abar_CastSpellByName(spellName, onSelf)
    Abar_OrigCastSpellByName(spellName, onSelf)

    for i = 1, 120 do
        if IsCurrentAction(i) then
            break
        end
        if i == 120 then return end
    end
    Abar_spell_direct(spellName)
end

CastSpellByName = Abar_CastSpellByName

-- Hook: UseAction(slot, checkCursor, onSelf)
Abar_OrigUseAction= UseAction

function Abar_UseAction(slot, checkCursor, onSelf)
    Abar_OrigUseAction(slot, checkCursor, onSelf)

    if GetActionText(slot) or not IsCurrentAction(slot) then
        return
    end

    local isUsable, notEnoughMana = IsUsableAction(slot)
    if isUsable then
        if UnitCanAttack("player", "target")then
            if IsActionInRange(slot) == 1 then
                Abar_Tooltip:ClearLines()
                Abar_Tooltip:SetAction(slot)
                local spellName = Abar_TooltipTextLeft1:GetText()
                if spellName then
                    Abar_spell_direct(spellName)
                end
            end
        end
    end
end

UseAction = Abar_UseAction

-- Hook: CastSpell(spellId, spellBookType)
Abar_OrigCastSpell = CastSpell

function Abar_CastSpell(spellId, spellBookType)
    Abar_OrigCastSpell(spellId, spellBookType)

    local spellName = GetSpellName(spellId, spellBookType)
    Abar_spell_direct(spellName)
end

CastSpell = Abar_CastSpell

-----------------------------------------------------------------------------------------------------------------------
-- ENEMY BAR CODE --
-----------------------------------------------------------------------------------------------------------------------

function ebar_VL()
    ebar_mh:SetPoint("LEFT", ebar_Frame, "TOPLEFT", 6, -13)
    ebar_oh:SetPoint("LEFT", ebar_Frame, "TOPLEFT", 6, -38)
    ebar_mhText:SetJustifyH("Left")
    ebar_ohText:SetJustifyH("Left")

    local fontName, fontSize = ebar_mhText:GetFont()
    ebar_mhText:SetFont(fontName, 11, "OUTLINE")
    ebar_ohText:SetFont(fontName, 11, "OUTLINE")

    ebar_mhTime:SetFont(fontName, 11, "OUTLINE")
    ebar_ohTime:SetFont(fontName, 11, "OUTLINE")
end

function ebar_start(arg1)
    local a, b, hitter = string.find(arg1, "(.+)击中你")
    if not hitter then
        a, b, hitter = string.find(arg1, "(.+)的致命一击对你")
    end
    if not hitter then
        a, b, hitter = string.find(arg1, "(.+)没有击中你")
    end
    if not hitter then
        a, b, hitter = string.find(arg1, "(.+)发起了攻击。你")
    end
    if hitter == UnitName("target") then
        ebar_set(hitter)
    end
end

function ebar_event(event)
    if event == "VARIABLES_LOADED" then
        ebar_VL()
    elseif (event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" or event == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES") and
        AttackBarDB.mob == true then
        ebar_start(arg1)
    elseif (event == "CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS" or event == "CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES") and
        AttackBarDB.pvp == true then
        ebar_start(arg1)
    end
end

function ebar_set(targ)
    eons, eoffs = UnitAttackSpeed("target")
    eons = eons - math_mod(eons, 0.01)
    ebar_mhs(eons, "目标 " .. "[" .. eons .. "秒]", 1, .1, .1)
end

function ebar_mhs(barTime, text, r, g, b)
    ebar_mh:Hide()
    ebar_mh.txt = text
    -- 减去网络延迟
    local _, _, latencyAb = GetNetStats()
    ebar_mh.st = GetTime() - (latencyAb / 1000)
    ebar_mh.et = GetTime() + barTime
    ebar_mh:SetStatusBarColor(r, g, b)
    ebar_mhText:SetText(text)
    ebar_mh:SetMinMaxValues(ebar_mh.st, ebar_mh.et)
    ebar_mh:SetValue(ebar_mh.st)
    ebar_mh:Show()
end

function ebar_ohs(barTime, text, r, g, b)
    ebar_oh:Hide()
    ebar_oh.txt = text
    -- 减去网络延迟
    local _, _, latencyAb = GetNetStats()
    ebar_oh.st = GetTime() - (latencyAb / 1000)
    ebar_oh.et = GetTime() + barTime
    ebar_oh:SetStatusBarColor(r, g, b)
    ebar_ohText:SetText(text)
    ebar_oh:SetMinMaxValues(ebar_oh.st, ebar_oh.et)
    ebar_oh:SetValue(ebar_oh.st)
    ebar_oh:Show()
end
